package xcmg.device.service.buyBack;

import com.github.pagehelper.PageInfo;
import yb.ecp.fast.infra.util.PageHelperPlus;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xcmg.device.dao.entity.buyBack.BackOutboundDO;
import xcmg.device.dao.entity.buyBack.BackOutboundDetailDO;
import xcmg.device.dao.mapper.buyBack.BackOutboundDetailMapper;
import xcmg.device.dao.mapper.buyBack.BackOutboundMapper;
import xcmg.device.dao.mapper.warehouse.WarehouseAreaLocationDOMapper;
import xcmg.device.dao.mapper.warehouse.WarehouseDeliveryDetailMapper;
import xcmg.device.infra.DateUtil;
import xcmg.device.infra.ErrorCode;
import xcmg.device.infra.ExportUtil;
import xcmg.device.infra.enums.BillDesc;
import xcmg.device.service.GenDocumentService;
import xcmg.device.service.vo.buyBack.BackInboundAddVO;
import xcmg.device.service.vo.buyBack.BackInboundDetailVO;
import xcmg.device.service.vo.buyBack.BackInboundVO;
import xcmg.device.service.vo.buyBack.BackOutboundVO;
import xcmg.device.service.warehouse.CommonService;
import xcmg.device.service.warehouse.WarehouseDeliveryService;
import yb.ecp.fast.feign.FastGenClient;
import yb.ecp.fast.infra.infra.ActionResult;
import yb.ecp.fast.infra.infra.PageCommonVO;
import yb.ecp.fast.infra.infra.SearchCommonVO;
import yb.ecp.fast.infra.util.ListUtil;
import yb.ecp.fast.infra.util.StringUtil;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by wrg on 2019/4/9.
 * 回购出库业务
 */
@Service
public class BackOutboundService {
    @Autowired
    private BackOutboundMapper backOutboundMapper;
    @Autowired
    private BackOutboundDetailMapper backOutboundDetailMapper;
    @Autowired
    private FastGenClient fastGenClient;
    @Autowired
    private BuyBackService buyBackService;
    @Autowired
    private GenDocumentService genDocumentService;
    @Autowired
    private CommonService commonService;
    @Autowired
    private WarehouseDeliveryService warehouseDeliveryService;
    @Autowired
    private HttpServletResponse response;
    @Autowired
    private WarehouseDeliveryDetailMapper warehouseDeliveryDetailMapper;
    @Autowired
    private WarehouseAreaLocationDOMapper warehouseAreaLocationDOMapper;

    @Transactional(rollbackFor = Exception.class)
    public ErrorCode add(BackInboundAddVO backInboundAddVO, String userId, String orgId)throws Exception{
        ActionResult<String> textResult = fastGenClient.textGuid();
        if (textResult.getCode() != ErrorCode.Success.getCode()) {
            return ErrorCode.IllegalArument;
        }

        //先判断入库意向数量是否大于剩余数量
        BigDecimal temp;
        for(BackInboundDetailVO detailVO : backInboundAddVO.getMaterialList()){
            temp = detailVO.getSendQty().add(detailVO.getAmount());
            if(temp.compareTo(detailVO.getQty())>0){
                throw new Exception("出库数量不能大于剩余数量");
            }
        }

        BackOutboundDO backInboundDO = new BackOutboundDO();
        BeanUtils.copyProperties(backInboundAddVO.getHeaderInfo(), backInboundDO);
        backInboundDO.setId(textResult.getValue());
        backInboundDO.setOutboundOrder(genDocumentService.getDocumentNo(BillDesc.BackOutbound));
        backInboundDO.setOrgId(orgId);
        backInboundDO.setCreateId(userId);
        backInboundDO.setCreateName(commonService.getUserName(userId));
        backInboundDO.setCreateDate(new Date());
        boolean success = backOutboundMapper.insertSelective(backInboundDO)>0;
        if(!success){
            throw new Exception("操作失败");
        }

        for(BackInboundDetailVO detialVO : backInboundAddVO.getMaterialList()){
            BackOutboundDetailDO detialDO = new BackOutboundDetailDO();
            BeanUtils.copyProperties(detialVO, detialDO);
            detialDO.setBillId(backInboundDO.getId());
            detialDO.setSendQty(detialDO.getSendQty().add(detialDO.getAmount()));
            success = backOutboundDetailMapper.insertSelective(detialDO)>0;
            if(!success){
                throw new Exception("操作失败");
            }

            //更新其他入库单的已入库意向数量
            backOutboundDetailMapper.updateSendQty(backInboundDO.getBackOrder(), detialDO.getMaterialsNo(),
                    detialDO.getSendQty(), orgId);

            //更新回购申请详情入库意向数量
            success = buyBackService.updateCreateOutQty(backInboundDO.getBackOrder(), detialDO.getMaterialsNo(), detialDO.getSendQty(), orgId);
            if(!success){
                throw new Exception("更新回购申请详情入库意向数量失败");
            }
        }

        //更新回购申请单的可否生成出库单状态
        success = buyBackService.updateOutboundEnable(backInboundDO.getBackOrder(), orgId);
        if(!success){
            throw new Exception("更新回购申请单的可否生成出库单状态失败");
        }

        BeanUtils.copyProperties(backInboundDO, backInboundAddVO.getHeaderInfo());
        backInboundAddVO.getHeaderInfo().setInboundOrder(backInboundDO.getOutboundOrder());
        success = warehouseDeliveryService.saveByBackOutbound(backInboundAddVO, userId, orgId);
        if(!success){
            throw new Exception("生成出库事物单失败");
        }
        return ErrorCode.Success;
    }

    public PageCommonVO list(SearchCommonVO<BackOutboundVO> condition) {
        PageCommonVO<BackOutboundVO> pageCommonVO = new PageCommonVO<>();
        PageHelperPlus.orderBy("create_date desc,id desc");
        PageHelperPlus.startPage(condition.getPageNum(), condition.getPageSize());
        List<BackOutboundDO> doList = backOutboundMapper.list(condition.getFilters());
        List<BackOutboundVO> voList = new ArrayList<>();
        for (BackOutboundDO entity : doList) {
            BackOutboundVO model = new BackOutboundVO();
            BeanUtils.copyProperties(entity, model);
            voList.add(model);
        }
        pageCommonVO.setPageInfo(new PageInfo<>(doList));
        pageCommonVO.setPageInfoList(voList);
        return pageCommonVO;
    }

    public BackInboundAddVO item(String id){
        BackInboundAddVO result = new BackInboundAddVO();
        BackOutboundDO backOutboundDO = backOutboundMapper.selectByPrimaryKey(id);
        if(backOutboundDO == null){
            return result;
        }
        BackInboundVO backInboundVO = new BackInboundVO();
        BeanUtils.copyProperties(backOutboundDO,backInboundVO);
        backInboundVO.setInboundOrder(backOutboundDO.getOutboundOrder());
        result.setHeaderInfo(backInboundVO);
        List<BackInboundDetailVO> listVO = new ArrayList<>();
        List<BackOutboundDetailDO> listDO = backOutboundDetailMapper.listForLocationUser(id);
        for(BackOutboundDetailDO entity:listDO){
            BackInboundDetailVO model = new BackInboundDetailVO();
            BeanUtils.copyProperties(entity,model);
            if (StringUtils.isNotBlank(model.getStockLocation())){
                model.setLocationDesc(warehouseAreaLocationDOMapper.getLocationDesc(model.getStockLocation()));
            }
            listVO.add(model);
        }
        result.setMaterialList(listVO);
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    public ErrorCode update(BackInboundAddVO backInboundAddVO) throws Exception{
        if(backInboundAddVO.getHeaderInfo() == null || ListUtil.isNullOrEmpty(backInboundAddVO.getMaterialList())){
            return ErrorCode.IllegalArument;
        }
        BackOutboundDO backOutboundDO = new BackOutboundDO();
        BeanUtils.copyProperties(backInboundAddVO.getHeaderInfo(), backOutboundDO);
        backOutboundDO.setOutboundOrder(backInboundAddVO.getHeaderInfo().getInboundOrder());
        //先判断入库数量是否大于剩余数量
        for(BackInboundDetailVO detailVO : backInboundAddVO.getMaterialList()){
            if(detailVO.getAmount().compareTo(detailVO.getRealAmount()) < 0){
                throw new Exception("计划出库数量不能小于实际出库数量");
            }
            BigDecimal temp = detailVO.getSendQty().subtract(detailVO.getLastAmount()).add(detailVO.getAmount());
            if(temp.compareTo(detailVO.getQty()) > 0){
                throw new Exception("出库数量不能大于剩余数量");
            }
        }

        boolean success;
        boolean allComplete = true;
        boolean someComplete = false;
        boolean manulComplete = false;
        for(BackInboundDetailVO detailVO : backInboundAddVO.getMaterialList()){
            BackOutboundDetailDO outboundDetailDO = backOutboundDetailMapper.findByMaterialNo(backOutboundDO.getId(), detailVO.getMaterialsNo());
            if(outboundDetailDO.getStatus() == 3){
                manulComplete = true;
            }
            if(outboundDetailDO.getStatus() == 2 || outboundDetailDO.getStatus() == 3){
                someComplete = true;
                continue;//已入库完成的零件或者结单的零件不可以编辑
            }

            BackOutboundDetailDO detailDO = new BackOutboundDetailDO();
            BeanUtils.copyProperties(detailVO,detailDO);
            detailDO.setSendQty(detailVO.getSendQty().subtract(detailVO.getLastAmount()).add(detailVO.getAmount()));

            if(detailDO.getAmount().compareTo(detailDO.getRealAmount()) > 0){
                if(allComplete){
                    allComplete = false;
                }
                if(detailDO.getRealAmount().compareTo(BigDecimal.ZERO) == 0){
                    detailDO.setStatus(0);
                }else{
                    detailDO.setStatus(1);
                    someComplete = true;
                }
            }else{
                someComplete = true;
                detailDO.setStatus(2);
            }

            success = backOutboundDetailMapper.update(detailDO)>0;
            if(!success){
                throw new Exception("更新回购出库详情记录失败");
            }

            //更新出库事物单详情记录
            ErrorCode errorCode = warehouseDeliveryService.updateDetailState(backOutboundDO.getOutboundOrder(), detailDO.getMaterialsNo(),
                    detailDO.getAmount(), detailDO.getStatus(), backOutboundDO.getOrgId());
            if(errorCode.getCode() != 0){
                throw new Exception("更新出库事物单详情记录失败");
            }

            //更新其他入库单的已入库意向数量
            backOutboundDetailMapper.updateSendQty(backOutboundDO.getBackOrder(), detailDO.getMaterialsNo(), detailDO.getSendQty(), backOutboundDO.getOrgId());

            //更新回购申请详情入库意向数量
            success = buyBackService.updateCreateOutQty(backOutboundDO.getBackOrder(), detailVO.getMaterialsNo(),
                    detailDO.getSendQty(), backOutboundDO.getOrgId());
            if(!success){
                throw new Exception("更新回购申请详情入库意向数量失败");
            }
        }
        if(allComplete){
            if(manulComplete){
                backOutboundDO.setStatus(3);
            }else{
                backOutboundDO.setStatus(2);
            }
        }else if(someComplete){
            backOutboundDO.setStatus(1);
        }else{
            backOutboundDO.setStatus(0);
        }
        success = backOutboundMapper.updateByPrimaryKeySelective(backOutboundDO)>0;
        if(!success){
            throw new Exception("更新回购入库记录失败");
        }

        //更新回购申请单的可否生成入库单状态
        success = buyBackService.updateOutboundEnable(backOutboundDO.getBackOrder(), backOutboundDO.getOrgId());
        if(!success){
            throw new Exception("更新回购申请单的可否生成入库单状态失败");
        }

        //更新出库事务单
        ErrorCode errorCode = warehouseDeliveryService.updateState(backOutboundDO.getOutboundOrder(), backOutboundDO.getStatus(),
                backOutboundDO.getOrgId());
        if(errorCode.getCode() != 0){
            throw new Exception("更新入库事务单记录失败");
        }
        return ErrorCode.Success;
    }

    @Transactional(rollbackFor = Exception.class)
    public ErrorCode remove(List<String> ids, String orgId) throws Exception{
        if(ListUtil.isNullOrEmpty(ids)){
            return ErrorCode.IllegalArument;
        }
        if(!ListUtil.isNullOrEmpty(backOutboundMapper.findForDeleteByIds(ids))){
            throw new Exception("只能删除未出库状态的回购出库单");
        }
        List<String> orderList = backOutboundMapper.findOrderList(ids);
        List<BackOutboundDO> backNoList = backOutboundMapper.findBackOrderList(ids);
        if(ListUtil.isNullOrEmpty(orderList) || ListUtil.isNullOrEmpty(backNoList)){
            return ErrorCode.IllegalArument;
        }
        Map<String, String> map = new HashMap<>();
        for(BackOutboundDO inboundDO : backNoList){
            map.put(inboundDO.getId(), inboundDO.getBackOrder());
        }
        List<BackOutboundDetailDO> detailDOList;
        boolean success;
        //更新回购申请单、其他回购入库单入库数量
        for(String id : ids){
            detailDOList = backOutboundDetailMapper.list(id);
            if(ListUtil.isNullOrEmpty(detailDOList)){
                throw new Exception("删除失败");
            }
            for(BackOutboundDetailDO detailDO : detailDOList){
                success = buyBackService.updateCreateOutQty(map.get(detailDO.getBillId()), detailDO.getMaterialsNo(),
                        detailDO.getSendQty().subtract(detailDO.getAmount()), orgId);
                if(!success){
                    if(ListUtil.isNullOrEmpty(detailDOList)){
                        throw new Exception("更新回购申请单入库意向数量失败");
                    }
                }
                //更新其他回购出库单的已出库意向数量
                backOutboundDetailMapper.updateSendQty(map.get(detailDO.getBillId()), detailDO.getMaterialsNo(), detailDO.getSendQty(), orgId);

            }
            //更新回购申请单的可否生成入库单状态
            success = buyBackService.updateOutboundEnable(map.get(id), orgId);
            if(!success){
                throw new Exception("更新回购申请单的创建入库单状态失败");
            }
        }
        success = backOutboundMapper.remove(ids)>0;
        if(!success){
            throw new Exception("删除主表记录失败");
        }
        success = backOutboundDetailMapper.deleteByBillIdList(ids)>0;
        if(!success){
            throw new Exception("删除详情记录失败");
        }
        //删除出库事物单
        success = warehouseDeliveryService.removeByBusinessOrders(orderList, orgId).getCode()==0;
        if(!success){
            throw new Exception("删除出库事物单失败");
        }
        return ErrorCode.Success;
    }

    public void export(List<String> ids)throws Exception{
        invokeExport(backOutboundMapper.findByIds(ids));
    }

    public void exportAll(BackOutboundVO condition)throws Exception{
        Integer count = backOutboundMapper.listCount(condition);
        List<BackOutboundDO> dataList = new ArrayList<>();
        int times = count/500+1;
        for(int i=0; i<times; i++){
            PageHelperPlus.startPage(i + 1, 500, "create_date desc");
            dataList.addAll(backOutboundMapper.list(condition));
        }
        invokeExport(dataList);
    }

    private void invokeExport(List<BackOutboundDO> doList)throws Exception{
        String title = "回购出库单数据导出";
        String[] rowsName = new String[]{"序号","回购出库单号","回购订单号","调出仓库","调出库区","出库状态", "制单人","制单时间"};
        List<Object[]>  dataList = new ArrayList<Object[]>();
        Object[] objs;
        for (int i = 0; i < doList.size(); i++) {
            BackOutboundDO exportDO = doList.get(i);
            objs = new Object[rowsName.length];
            objs[0] = i+1;
            objs[1] = exportDO.getOutboundOrder();
            objs[2] = exportDO.getBackOrder();
            objs[3] = exportDO.getWarehouseCode();
            objs[4] = exportDO.getInArea();
            objs[5] = getInboundState(exportDO.getStatus());
            objs[6] = exportDO.getCreateName();
            objs[7] = DateUtil.formatDateTime(exportDO.getCreateDate());
            dataList.add(objs);
        }
        ExportUtil exportUtil = new ExportUtil(title, rowsName, dataList);
        exportUtil.export(response);
    }

    private String getInboundState(Integer state){
        switch (state){
            case 0:
                return "未出库";
            case 1:
                return "部分出库";
            case 2:
                return "已出库";
            case 3:
                return "强制结单";
            default:
                return "未出库";
        }
    }

    /**
     * 手动完成单据<br>
     *     materialsNo 不为null,则更改该单据下的零件状态为“手动完成”状态；如果materialsNo为空，则更改整个单据为“手动完成”状态
     * @param billNo  String 单据编号
     * @param materialsNo String 零件编号
     * @param orgId String 企业ID
     * @return Er11rorCode 返回码
     */
    @Transactional(rollbackFor = Exception.class)
    public ErrorCode manualFinish(String billNo, String materialsNo, String orgId)throws Exception
    {
        BackOutboundDO outboundOrder = backOutboundMapper.findByOutboundOrder(billNo, orgId);
        if(outboundOrder == null){
            return ErrorCode.IllegalArument;
        }
        boolean success = buyBackService.updateOutboundByManulFinish(outboundOrder, materialsNo, orgId);
        if(!success){
            throw new Exception("更新回购申请状态失败");
        }

        if (StringUtil.isNullOrEmpty(materialsNo)){ //零件编码为空，更新单据状态为“手动完成”
            backOutboundMapper.updateStatus(billNo, 3, orgId);
            warehouseDeliveryService.updateState(billNo, 3, orgId);
            backOutboundDetailMapper.updateStatus(outboundOrder.getId(), null, 3);
            warehouseDeliveryDetailMapper.updateAllStatus(3, billNo, orgId);
        }else {
            backOutboundDetailMapper.updateStatus(outboundOrder.getId(), materialsNo, 3);
            warehouseDeliveryService.updateDetailState(billNo, materialsNo,null,3, orgId);
            ErrorCode errorCode = checkBillFinished(outboundOrder.getId(), outboundOrder.getOutboundOrder(), orgId);

            if(ErrorCode.Success!=errorCode)
            {
                return errorCode;
            }
        }
        return ErrorCode.Success;
    }

    /**
     * 校验单据是否已完成<br>
     *     判断单据下的所有零件是否已经完成，如果都完成，则更改单据状态为完成
     * @param billNo String
     * @param orgId String
     * @return Boolean
     */
    @Transactional(rollbackFor = Exception.class)
    private ErrorCode checkBillFinished(String billId, String billNo, String orgId) throws Exception {
        List<BackOutboundDetailDO> inboundDetailDOs = backOutboundDetailMapper.list(billId);
        if(ListUtil.isNullOrEmpty(inboundDetailDOs)){
            throw new Exception("未查询到回购入库单详情列表");
        }
        boolean manul = false;
        boolean flag = true;
        int status;
        for (BackOutboundDetailDO temp : inboundDetailDOs) {
            if(3==temp.getStatus()){
                manul = true;
            }
            if (2==temp.getStatus() || 3==temp.getStatus())
            {
                //完成状态
                continue;
            }
            flag =false;
        }
        if(flag){
            if(manul){
                status = 3;
            }else{
                status = 2;
            }
        }else{
            status = 1;
        }
        backOutboundMapper.updateStatus(billNo, status, orgId);
        warehouseDeliveryService.updateState(billNo, status, orgId);
        return ErrorCode.Success;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean updateByOutbound(String backOrder, String materialNo, BigDecimal realAmount,
                                   Integer singelState, Integer totalState, String orgId)throws Exception{
        BackOutboundDO inboundDO = backOutboundMapper.findByOutboundOrder(backOrder, orgId);
        if(inboundDO == null){
            return false;
        }
        BackOutboundDetailDO detailDO = backOutboundDetailMapper.findByMaterialNo(inboundDO.getId(), materialNo);
        if(detailDO == null){
            return false;
        }
        BigDecimal nowAmount = realAmount;
        if(detailDO.getRealAmount() != null){
            nowAmount = realAmount.subtract(detailDO.getRealAmount());
        }
        detailDO.setRealAmount(realAmount);
        detailDO.setStatus(singelState);
        boolean success = backOutboundDetailMapper.update(detailDO)>0;
        if(success){
            //更新回购入库单记录
            inboundDO.setStatus(totalState);
            success = backOutboundMapper.updateByPrimaryKeySelective(inboundDO)>0;
            if(!success){
                return false;
            }

            //更新回购申请单状态
            success = buyBackService.updateByOutBound(inboundDO.getBackOrder(), materialNo, nowAmount, orgId);

        }
        return success;
    }
}
